/**
 * 
 */
package com.uidp.uitils;

import java.util.Map;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItems;
import javax.faces.component.html.HtmlCommandButton;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGrid;
import javax.faces.component.html.HtmlPanelGroup;
import javax.faces.component.html.HtmlSelectBooleanCheckbox;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionListener;

import org.ajax4jsf.component.AjaxActionComponent;
import org.ajax4jsf.component.html.HtmlActionParameter;
import org.ajax4jsf.component.html.HtmlAjaxCommandLink;
import org.apache.myfaces.event.SetPropertyActionListener;
import org.richfaces.component.html.HtmlCalendar;
import org.richfaces.component.html.HtmlMenuItem;
import org.richfaces.component.html.HtmlPanel;
import org.richfaces.component.html.HtmlSeparator;
import org.richfaces.component.html.HtmlSpacer;
import org.richfaces.component.html.HtmlToolTip;

import com.acsior.wu.utils.UIUtils;


/**
 * Предлага услуги за генериране на JSF компоненти
 * 
 */
public class JSFComponentsBuilder {

	/**
	 * Атрибут "datePattern" на компонент Календар
	 */
	public static String DATE_PATTERN = "datePattern";

	/***/
	public static String HEIGHT = "5px";

	/**
	 * Префикс на ИД на панел
	 */
	public static String ID_PREFIX_LINK = "lnk_";

	/**
	 * Префикс на ИД на панел
	 */
	public static String ID_PREFIX_PANEL = "pnl_";

	/**
	 * Префикс на ИД на панел
	 */
	public static String ID_PREFIX_PARAM = "prm_";

	/***/
	public static String JOINT_POINT = "top-left";

	/***/
	public static String LINE_TYPE = "beveled";

	/**
	 * styleclass = .bold
	 */
	public static String STYLECLASS_BOLD = "bold";

	/**
	 * styleclass = .error-style-bold
	 */
	public static String STYLECLASS_ERROR_STYLE_BOLD = "error-style-bold";

	/**
	 * Атрибут "value" на компонент
	 */
	public static String VALUE = "value";

	/***/
	public static String WIDTH = "100%";

	/**
	 * Генерира компонент - HtmlAjaxCommandLink
	 * 
	 * @param app
	 *            - {@link Application}
	 * @param label
	 *            - етикет (или null)
	 * @param id
	 *            - ид (или null)
	 * @param title
	 *            - заглваие (или null)
	 * @param parent
	 *            - родителски компонент (или null)
	 * @param action
	 *            - действие (или null)
	 * @param listener
	 *            - лисънър (или null)
	 * @param ajaxSingle
	 *            - вид на request (или null)
	 * @param rendered
	 *            - дали да се визуализира (или null)
	 * @param reRender
	 *            - ид на компонент, който да се опреснява при клик(или null)
	 * @param params
	 *            - параметри, който се добавят като {@link HtmlActionParameter}
	 * @param styleClass
	 *            - стил
	 * @return UIAjaxCommandLink - генериран линк
	 */
	public static HtmlAjaxCommandLink buildAjaxCommandLink(Application app, String id,
			String label, String title, UIComponent parent, MethodExpression action,
			ActionListener listener, Boolean ajaxSingle, Boolean rendered, String reRender,
			Map<String, String> params, String styleClass) {
		HtmlAjaxCommandLink comp = (HtmlAjaxCommandLink) createComponent(app,
				HtmlAjaxCommandLink.COMPONENT_TYPE, id);
		setValue(comp, label);
		setTitle(comp, title);
		setParent(comp, parent);
		addActionListener(comp, listener);
		addAction(comp, action);
		setRendered(comp, rendered);
		setAjaxSingle(comp, ajaxSingle);
		setReRender(comp, reRender);
		if (isNotEmpty(styleClass)) {
			comp.setStyleClass(styleClass);
		}

		if (params != null && !params.isEmpty()) {
			SetPropertyActionListener propListener = null;
			for (Map.Entry<String, String> entry : params.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				propListener = buildPropertyActionListener(app, key, value);
				comp.addActionListener(propListener);
			}

		}
		return comp;
	}

	/**
	 * Генерира {@link HtmlActionParameter}
	 * 
	 * @param app
	 * @param id
	 * @param name
	 * @param value
	 * @param assignToExpr
	 * @param parent
	 * @return HtmlActionParameter
	 */
	public static HtmlActionParameter buildHActionParameter(Application app, String id,
			String name, String value, String assignToExpr, UIComponent parent) {
		HtmlActionParameter comp = new HtmlActionParameter();
		setId(comp, id);
		setParent(comp, parent);
		comp.setName(name);
		comp.setValue(value);
		comp.setAssignToBinding(buildValueExpression(app, assignToExpr));
		return comp;
	}

	/**
	 * Генерира компонент - HtmlCommandButton
	 * 
	 * @param app
	 *            - {@link Application}
	 * @param label
	 *            - етикет (или null)
	 * @param id
	 *            - ид (или null)
	 * @param parent
	 *            - родителски компонент (или null)
	 * @param action
	 *            - действие (или null)
	 * @param listener
	 *            - лисънър (или null)
	 * @param rendered
	 *            - дали да се визуализира (или null)
	 * @param params
	 *            - параметри, който се добавят като {@link HtmlActionParameter}
	 * @param styleClass
	 *            - стил
	 * @param js
	 *            - JS, който се добавя на Onclick
	 * @return HtmlCommandButton - генериран линк
	 */
	public static HtmlCommandButton buildHCommandButton(Application app, String id, String label,
			UIComponent parent, MethodExpression action, ActionListener listener, String rendered,
			Map<String, String> params, String styleClass, String js) {
		HtmlCommandButton comp = (HtmlCommandButton) createComponent(app,
				HtmlCommandButton.COMPONENT_TYPE, id);
		setValue(comp, label);
		setParent(comp, parent);
		addActionListener(comp, listener);
		addAction(comp, action);
		setRendered(rendered, comp);
		if (styleClass != null) {
			comp.setStyleClass(styleClass);
		}

		if (isNotEmpty(js)) {
			comp.setOnclick(js);
		}

		if (params != null && !params.isEmpty()) {
			SetPropertyActionListener propListener = null;
			for (Map.Entry<String, String> entry : params.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				propListener = buildPropertyActionListener(app, key, value);
				comp.addActionListener(propListener);
			}

		}
		return comp;
	}

	/**
	 * Генерира компонент - HtmlCommandLink
	 * 
	 * @param app
	 *            - {@link Application}
	 * @param label
	 *            - етикет (или null)
	 * @param id
	 *            - ид (или null)
	 * @param parent
	 *            - родителски компонент (или null)
	 * @param action
	 *            - действие (или null)
	 * @param listener
	 *            - лисънър (или null)
	 * @param renderedExpression
	 *            - условие за визуализация, трябва да иглежда по следния начин: <br>
	 *            1) "true" - ако е булева стойност<br>
	 *            2) "user.visible" - ако е израз, където visible е булево
	 *            пропърти на MB - user
	 * @param params
	 *            - параметри, който се добавят като {@link HtmlActionParameter}
	 * @param styleClass
	 *            - стил
	 * @return UIAjaxCommandLink - генериран линк
	 */
	public static HtmlCommandLink buildHCommandLink(Application app, String id, String label,
			UIComponent parent, MethodExpression action, ActionListener listener,
			Map<String, String> params, String styleClass, String renderedExpression) {
		HtmlCommandLink comp = (HtmlCommandLink) createComponent(app,
				HtmlCommandLink.COMPONENT_TYPE, id);
		setValue(comp, label);
		setParent(comp, parent);
		addActionListener(comp, listener);
		addAction(comp, action);
		if (styleClass != null) {
			comp.setStyleClass(styleClass);
		}

		if (params != null && !params.isEmpty()) {
			SetPropertyActionListener propListener = null;
			for (Map.Entry<String, String> entry : params.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				propListener = buildPropertyActionListener(app, key, value);
				comp.addActionListener(propListener);
			}
		}
		setRendered(renderedExpression, comp);
		return comp;
	}

	/**
	 * Генерира компонент - HtmlInputText
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param valueExpr
	 * @return - HtmlInputText
	 */
	public static HtmlInputText buildHInputText(Application app, UIComponent parent, String id,
			ValueExpression valueExpr) {
		HtmlInputText comp = (HtmlInputText) createComponent(app, HtmlInputText.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);
		if (valueExpr != null) {
			comp.setValueExpression(VALUE, valueExpr);
		}
		return comp;
	}

	/**
	 * Генерира компонент - HtmlOutputText
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param value
	 *            - стойност
	 * @return - HtmlOutputText
	 */
	public static HtmlOutputText buildHOutputText(Application app, UIComponent parent, String id,
			String value) {
		HtmlOutputText comp = (HtmlOutputText) createComponent(app, HtmlOutputText.COMPONENT_TYPE,
				id);
		setParent(comp, parent);
		setId(comp, id);
		comp.setValue(value);
		return comp;
	}

	/**
	 * Генерира компонент - HtmlOutputText
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param value
	 *            - стойност
	 * @param styleClass
	 *            - стил за класа
	 * @return - HtmlOutputText
	 */
	public static HtmlOutputText buildHOutputText(Application app, UIComponent parent, String id,
			String value, String styleClass) {
		HtmlOutputText comp = buildHOutputText(app, parent, id, value);
		comp.setStyleClass(styleClass);
		return comp;
	}

	/**
	 * Генерира компонент - HtmlPanelGrid
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param columns
	 *            - брой колони или NULL за стойност по подразбиране
	 * @return - HtmlPanelGrid
	 */
	public static HtmlPanelGrid buildHPanelGrid(Application app, UIComponent parent, String id,
			Integer columns) {
		return buildHPanelGrid(app, parent, id, columns, null);
	}

	/**
	 * Генерира компонент - HtmlPanelGrid
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param columns
	 *            - брой колони или NULL за стойност по подразбиране
	 * @param border
	 *            - int
	 * @return - HtmlPanelGrid
	 */
	public static HtmlPanelGrid buildHPanelGrid(Application app, UIComponent parent, String id,
			Integer columns, Integer border) {
		HtmlPanelGrid comp = (HtmlPanelGrid) createComponent(app, HtmlPanelGrid.COMPONENT_TYPE, id);
		if (columns != null) {
			comp.setColumns(columns.intValue());
		}
		setParent(comp, parent);
		if (border != null) {
			comp.setBorder(border);
		}
		return comp;
	}

	/**
	 * Генерира компонент - HtmlPanelGroup
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @return - HtmlPanelGrid
	 */
	public static HtmlPanelGroup buildHPanelGroup(Application app, UIComponent parent, String id) {
		HtmlPanelGroup comp = (HtmlPanelGroup) createComponent(app, HtmlPanelGroup.COMPONENT_TYPE,
				id);
		setParent(comp, parent);
		setId(comp, id);

		return comp;
	}

	/**
	 * Генерира компонент - HtmlSelectBooleanCheckbox
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param title
	 *            - информация за елемента
	 * @param valueExpr
	 * @return - HtmlSelectBooleanCheckbox
	 */
	public static HtmlSelectBooleanCheckbox buildHSelectBooleanCheckbox(Application app,
			UIComponent parent, String id, String title, ValueExpression valueExpr) {
		HtmlSelectBooleanCheckbox comp = (HtmlSelectBooleanCheckbox) createComponent(app,
				HtmlSelectBooleanCheckbox.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);
		comp.setTitle(title);
		if (valueExpr != null) {
			comp.setValueExpression(VALUE, valueExpr);
		}
		return comp;
	}

	/**
	 * Генерира компонент - HtmlSelectOneMenu
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param items
	 *            - информация за елемента
	 * @param valueExpr
	 * @return - HtmlSelectOneMenu
	 */
	public static HtmlSelectOneMenu buildHSelectOneMenu(Application app, UIComponent parent,
			String id, UISelectItems items, ValueExpression valueExpr) {
		HtmlSelectOneMenu comp = (HtmlSelectOneMenu) createComponent(app,
				HtmlSelectOneMenu.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);

		if (valueExpr != null) {
			comp.setValueExpression(VALUE, valueExpr);
		}
		comp.getChildren().add(items);

		return comp;
	}

	/**
	 * Генерира {@link MethodExpression}
	 * 
	 * @param app
	 * @param value
	 * @return MethodExpression
	 */
	public static MethodExpression buildMethodExpression(Application app, String value) {
		MethodExpression actionExpression = null;
		if (value != null) {
			actionExpression = app.getExpressionFactory().createMethodExpression(
					FacesContext.getCurrentInstance().getELContext(), value, String.class,
					new Class[] {});
		}
		return actionExpression;
	}

	/**
	 * Генерира {@link SetPropertyActionListener}
	 * 
	 * @param app
	 * @param target
	 * @param value
	 * @return ActionListener
	 */
	public static SetPropertyActionListener buildPropertyActionListener(Application app,
			String target, String value) {
		ValueExpression valueExpr = buildValueExpression(app, value);
		ValueExpression targetExpr = buildValueExpression(app, target);
		return new SetPropertyActionListener(targetExpr, valueExpr);
	}

	/**
	 * Генерира компонент - HtmlCalendar
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param valueExpr
	 *            - ValueExpression на атрибут VALUE
	 * @param patternExpr
	 *            - ValueExpression на атрибут DATE_PATTERN
	 * @return - HtmlCalendar
	 */
	public static HtmlCalendar buildRichCalendar(Application app, UIComponent parent, String id,
			ValueExpression valueExpr, ValueExpression patternExpr) {
		HtmlCalendar comp = (HtmlCalendar) createComponent(app, HtmlCalendar.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);
		comp.setFirstWeekDay(1);
		comp.setInputSize(10);
		comp.setJointPoint(JOINT_POINT);
		comp.setValueExpression(DATE_PATTERN, patternExpr);
		comp.setValueExpression(VALUE, valueExpr);
		return comp;
	}

	/**
	 * Генерира компонент - HtmlCalendar
	 * 
	 * @param app
	 *            {@link Application}
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД или null
	 * @param value
	 *            - стойност/етикет
	 * @param renderedExpression
	 *            - условие за визуализация, трябва да иглежда по следния начин: <br>
	 *            1) "true" - ако е булева стойност<br>
	 *            2) "user.visible" - ако е израз, където visible е булево
	 *            пропърти на MB - user
	 * @param js
	 *            - JS, който се добавя на "onclick"
	 * @param submitMode
	 * @return {@link HtmlMenuItem}
	 */
	public static HtmlMenuItem buildRichMenuItem(Application app, UIComponent parent, String id,
			String value, String js, String submitMode, String renderedExpression) {
		HtmlMenuItem comp = (HtmlMenuItem) app.createComponent(HtmlMenuItem.COMPONENT_TYPE);
		setParent(comp, parent);
		setId(comp, id);
		if (isNotEmpty(value)) {
			comp.setValue(value);
		}
		if (isNotEmpty(js)) {
			comp.setOnclick(js);
		}
		if (isNotEmpty(submitMode)) {
			comp.setSubmitMode(submitMode);
		}
		setRendered(renderedExpression, comp);
		return comp;

	}

	/**
	 * Генерира компонент - HtmlPanel
	 * 
	 * @param app
	 *            {@link Application}
	 * @param id
	 *            - ИД или null
	 * @return - HtmlPanelGrid
	 */
	public static HtmlPanel buildRichPanel(Application app, String id) {
		return (HtmlPanel) createComponent(app, HtmlPanel.COMPONENT_TYPE, id);
	}

	/**
	 * Генерира {@link HtmlSeparator}
	 * 
	 * @param app
	 * @param parent
	 * @param id
	 * @param title
	 * @return MethodExpression
	 */
	public static HtmlSeparator buildRichSeparator(Application app, UIComponent parent, String id,
			String title) {
		HtmlSeparator comp = (HtmlSeparator) createComponent(app, HtmlSeparator.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);

		comp.setLineType(LINE_TYPE);
		comp.setWidth(WIDTH);
		comp.setHeight(HEIGHT);
		comp.setTitle(title);
		return comp;
	}

	/**
	 * Генерира {@link HtmlSeparator}
	 * 
	 * @param app
	 * @param parent
	 * @param id
	 * @param width
	 * @param height
	 * @return MethodExpression
	 */
	public static HtmlSpacer buildRichSpacer(Application app, UIComponent parent, String id,
			String width, String height) {
		HtmlSpacer comp = (HtmlSpacer) createComponent(app, HtmlSpacer.COMPONENT_TYPE, id);
		setParent(comp, parent);
		setId(comp, id);

		if (UIUtils.isNotEmpty(width)) {
			comp.setWidth(width);
		}

		if (UIUtils.isNotEmpty(height)) {
			comp.setHeight(height);
		}
		return comp;
	}

	/**
	 * Генерира компонент - HtmlToolTip
	 * 
	 * @param app
	 *            {@link Application}
	 * @param value
	 *            - Текст
	 * @param parent
	 *            - родителски компонент
	 * @param id
	 *            - ИД
	 * @param styleClass
	 *            - стил
	 * @return HtmlToolTip
	 */
	public static HtmlToolTip buildRichToolTip(Application app, String value, UIComponent parent,
			String id, String styleClass) {

		HtmlToolTip comp = (HtmlToolTip) createComponent(app, HtmlToolTip.COMPONENT_TYPE, id);
		setValue(comp, value);
		setParent(comp, parent);
		if (styleClass != null) {
			comp.setStyleClass(styleClass);
		}
		return comp;

	}

	/**
	 * Генерира {@link ValueExpression}
	 * 
	 * @param app
	 * @param value
	 * @return ValueExpression
	 */
	public static ValueExpression buildValueExpression(Application app, String value) {
		ValueExpression valueExpression = app.getExpressionFactory().createValueExpression(
				FacesContext.getCurrentInstance().getELContext(), value, String.class);
		return valueExpression;
	}

	/**
	 * Генерира {@link ValueExpression}
	 * 
	 * @param ctx
	 * @param value
	 * @param expectedClass
	 * @return MethodExpression
	 */
	public static ValueExpression buildValueExpression(FacesContext ctx, String value,
			Class<?> expectedClass) {
		ELContext elctx = ctx.getELContext();
		ExpressionFactory exprFactory = ctx.getApplication().getExpressionFactory();
		ValueExpression vExpr = exprFactory.createValueExpression(elctx, "#{" + value + "}",
				expectedClass);
		return vExpr;
	}

	/**
	 * Генерира {@link ValueExpression}
	 * 
	 * @param ctx
	 * @param mbName
	 * @param value
	 * @param expectedClass
	 * @return MethodExpression
	 */
	public static ValueExpression buildValueExpression(FacesContext ctx, String mbName,
			String value, Class<?> expectedClass) {
		return buildValueExpression(ctx, mbName + "." + value, expectedClass);
	}

	/**
	 * @param renderedExpression
	 * @param comp
	 */
	public static void setRendered(String renderedExpression, UIComponent comp) {
		if (isNotEmpty(renderedExpression)) {
			comp.setValueExpression(
					"rendered",
					buildValueExpression(FacesContext.getCurrentInstance(), renderedExpression,
							Boolean.class));
		}
	}

	private static void addAction(UICommand comp, MethodExpression action) {
		if (action != null) {
			comp.setActionExpression(action);
		}
	}

	private static void addActionListener(UICommand comp, ActionListener listener) {
		if (listener != null) {
			comp.addActionListener(listener);
		}
	}

	private static UIComponent createComponent(Application app, String componentType, String id) {
		UIComponent comp = app.createComponent(componentType);
		setId(comp, id);
		return comp;
	}

	private static boolean isNotEmpty(String s) {
		return s != null && !s.trim().equals("");
	}

	private static void setAjaxSingle(AjaxActionComponent comp, Boolean ajaxSingle) {
		if (ajaxSingle != null) {
			comp.setAjaxSingle(ajaxSingle.booleanValue());
		}
	}

	private static void setId(UIComponent comp, String id) {
		if (id != null && !id.trim().equals("")) {
			comp.setId(id);
		}
	}

	private static void setParent(UIComponent comp, UIComponent parent) {
		if (parent != null) {
			comp.setParent(parent);
		}
	}

	private static void setRendered(UICommand comp, Boolean rendered) {
		if (rendered != null) {
			comp.setRendered(rendered.booleanValue());
		}
	}

	private static void setReRender(AjaxActionComponent comp, Object reRender) {
		if (reRender != null) {
			comp.setReRender(reRender);
		}
	}

	private static void setTitle(HtmlAjaxCommandLink comp, String title) {
		if (title != null) {
			comp.setTitle(title);
		}
	}

	private static void setValue(UICommand comp, String value) {
		if (value != null) {
			comp.setValue(value);
		}
	}

}
